#!/usr/bin/python3
# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: t -*-

import os
import sys
import json
import select
import shutil
import pathlib
sys.path.append('/usr/lib64/syncupd')
from gbs_util import GbsUtil
from gbs_param import GbsConst
from gbs_param import GbsParam
from gbs_common import GbsProtocolException
from gbs_ctrl_session import GbsCtrlSession


# these directories should be already created by syncupd main process
assert os.path.exists(GbsConst.runDir)
assert os.path.exists(GbsConst.varDir)
assert os.path.exists(GbsConst.logDir)

# disable buffering for stdin/stdout to ensure immediate data transfer
sys.stdin = sys.stdin.buffer
sys.stdout = sys.stdout.buffer

param = GbsParam()
sessionObj = None
try:
    # get user
    user = os.environ["USER"]

    # get client ip:port
    tlist = os.environ["SSH_CLIENT"].split()
    clientIp = tlist[0]
    clientPort = tlist[1]

    # get client public key
    clientPubKey = pathlib.Path(os.environ["SSH_USER_AUTH"]).read_text()
    assert clientPubKey.startswith("publickey ")                            # the generated sshd configuration ensures that SSH_USER_AUTH exists and publickey auth must be used
    clientPubKey = clientPubKey[len("publickey "):]

    # run session
    recvBuf = b''
    sendBuf = b''
    sessionObj = GbsCtrlSession(param, user, clientIp, clientPort, clientPubKey)
    print("Main Server: Client \"%s\" appeared, UUID \"%s\"." % (clientIp, sessionObj.sysObj.uuid), file=sys.stderr)
    while True:
        # select
        inputs = [sys.stdin]
        if len(sendBuf) > 0:
            outputs = [sys.stdout]
        else:
            outputs = []
        readable, writable, exceptional = select.select(inputs, outputs, inputs, 10.0)

        # read from client, process request
        while len(readable) > 0:
            try:
                buf = sys.stdin.read(4096)
                if len(buf) == 0:
                    print("Control Channel: Client \"%s\" disconnected." % (self._formatClient()), file=sys.stderr)
                    break
            except BaseException as e:
                # FIXME: what kind of exception can be catched here
                print("debug %s" % (e), file=sys.stderr)
                break

            recvBuf += buf
            i = recvBuf.find(b'\n')
            if i < 0:
                # no full json object received
                continue

            requestObj = json.loads(recvBuf[:i].decode("iso8859-1"))           # ignore "\n"
            recvBuf = recvBuf[i + 1:]                                          # jump over "\n"

            # process the received request json object
            try:
                responseObj = sessionObj.onRequest(requestObj)                 # create response when processing request
            except GbsProtocolException as e:
                print("Control Channel: Bad request from client \"%s\", %s." % (self._formatClient(), str(e)), file=sys.stderr)
                break

            sendBuf += (json.dumps(responseObj) + "\n").encode("iso8859-1")

        # write to client
        if len(writable) > 0:
            i = sys.stdin.write(sendBuf)
            sendBuf = sendBuf[i:]

        # error occured
        if len(exceptional) > 0:
            # FIXME: what kind of exception can be catched here
            print("debug2", file=sys.stderr)
            break
except BaseException:
    # FIXME: we should catch exception for gbs_system lock and print error message to sys.stderr
    raise
finally:
    if sessionObj is not None:
        sessionObj.dispose()
